package com.pickgliss.ui.controls
{
	import com.pickgliss.events.InteractiveEvent;
	import com.pickgliss.geom.InnerRectangle;
	import com.pickgliss.geom.IntPoint;
	import com.pickgliss.toplevel.StageReferance;
	import com.pickgliss.ui.ComponentFactory;
	import com.pickgliss.ui.ComponentSetting;
	import com.pickgliss.ui.core.Component;
	import com.pickgliss.ui.core.IOrientable;
	import com.pickgliss.utils.ClassUtils;
	import com.pickgliss.utils.DisplayUtils;
	import com.pickgliss.utils.ObjectUtils;
	import flash.display.DisplayObject;
	import flash.display.InteractiveObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.Timer;
	public class Scrollbar extends Component implements IOrientable
	{
		public static const HORIZONTAL:int = 1;
		public static const P_decreaseButton:String = "decreaseButton";
		public static const P_decreaseButtonInnerRect:String = "decreaseButtonInnerRect";
		public static const P_increaseButton:String = "increaseButton";
		public static const P_increaseButtonInnerRect:String = "increaseButtonInnerRect";
		public static const P_maximum:String = "maximum";
		public static const P_minimum:String = "minimum";
		public static const P_orientation:String = "orientation";
		public static const P_scrollValue:String = "scrollValue";
		public static const P_thumb:String = "thumb";
		public static const P_thumbAreaInnerRect:String = "thumbAreaInnerRect";
		public static const P_thumbMinSize:String = "thumbMinSize";
		public static const P_track:String = "track";
		public static const P_trackInnerRect:String = "trackInnerRect";
		public static const P_valueIsAdjusting:String = "valueIsAdjusting";
		public static const P_visibleAmount:String = "visibleAmount";
		public static const VERTICAL:int = 0;
		
		public function Scrollbar()
		{
			super();
		}

		protected var _blockIncrement:int = 20;
		protected var _currentTrackClickDirction:int = 0;
		protected var _decreaseButton:BaseButton;
		protected var _decreaseButtonInnerRect:InnerRectangle;
		protected var _decreaseButtonInnerRectString:String;
		protected var _decreaseButtonStyle:String;
		protected var _increaseButton:BaseButton;
		protected var _increaseButtonInnerRect:InnerRectangle;
		protected var _increaseButtonInnerRectString:String;
		protected var _increaseButtonStyle:String;
		protected var _isDragging:Boolean;
		protected var _model:BoundedRangeModel;
		protected var _orientation:int;
		protected var _thumb:BaseButton;
		protected var _thumbAreaInnerRect:InnerRectangle;
		protected var _thumbAreaInnerRectString:String;
		protected var _thumbDownOffset:int;
		protected var _thumbMinSize:int;
		protected var _thumbRect:Rectangle;
		protected var _thumbStyle:String;
		protected var _track:DisplayObject;
		protected var _trackClickTimer:Timer;
		protected var _trackInnerRect:InnerRectangle;
		protected var _trackInnerRectString:String;
		protected var _trackStyle:String;
		protected var _unitIncrement:int = 2;
		
		public function addStateListener(listener:Function, priority:int=0, useWeakReference:Boolean=false):void{
			addEventListener(InteractiveEvent.STATE_CHANGED, listener, false, priority);
		}
		
		public function get blockIncrement():int{
			return _blockIncrement;
		}
		public function set blockIncrement(blockIncrement:int):void{
			_blockIncrement = blockIncrement;
		}
		/**
		 * 
		 * @param display 向上按钮
		 * 
		 */		
		public function set decreaseButton(display:BaseButton):void
		{
			if(_decreaseButton == display) return;
			if(_decreaseButton)_decreaseButton.removeEventListener(Event.CHANGE,__increaseButtonClicked);
			ObjectUtils.disposeObject(_decreaseButton);
			_decreaseButton = display;
			if(_decreaseButton)_decreaseButton.pressEnable = true;
			if(_decreaseButton)_decreaseButton.addEventListener(Event.CHANGE,__increaseButtonClicked);
			onPropertiesChanged(P_decreaseButton);
		}
		public function set decreaseButtonInnerRectString(value:String):void
		{
			if(_decreaseButtonInnerRectString == value)return;
			_decreaseButtonInnerRectString = value;
			_decreaseButtonInnerRect = ClassUtils.CreatInstance(ClassUtils.INNERRECTANGLE,ComponentFactory.parasArgs(_decreaseButtonInnerRectString));
			onPropertiesChanged(P_decreaseButtonInnerRect);
		}
		
		/**
		 * 
		 * @param stylename 向上按钮的样式
		 * 
		 */		
		public function set decreaseButtonStyle(stylename:String):void
		{
			if(_decreaseButtonStyle == stylename) return;
			_decreaseButtonStyle = stylename;
			decreaseButton = ComponentFactory.Instance.creat(_decreaseButtonStyle);
		}
		
		override public function dispose():void
		{
			if(_thumb)_thumb.removeEventListener(MouseEvent.MOUSE_DOWN,__onThumbDown);
			ObjectUtils.disposeObject(_thumb);
			_thumb = null;
			
			if(_decreaseButton)_decreaseButton.removeEventListener(Event.CHANGE,__decreaseButtonClicked);
			ObjectUtils.disposeObject(_decreaseButton);
			_decreaseButton = null;
			
			if(_increaseButton)_increaseButton.removeEventListener(Event.CHANGE,__increaseButtonClicked);
			ObjectUtils.disposeObject(_increaseButton);
			_increaseButton = null;
			
			StageReferance.stage.removeEventListener(MouseEvent.MOUSE_UP,__onThumbUp);
			StageReferance.stage.removeEventListener(MouseEvent.MOUSE_MOVE,__onThumbMoved);
			
			if(_track && _track is InteractiveObject)
			{
				_track.removeEventListener(MouseEvent.MOUSE_DOWN,__onTrackClickStart);
				_track.removeEventListener(MouseEvent.MOUSE_UP,__onTrackClickStop);
				_track.removeEventListener(MouseEvent.MOUSE_OUT,__onTrackClickStop);
				_trackClickTimer.stop();
				_trackClickTimer.removeEventListener(TimerEvent.TIMER,__onTrackPressed);
			}
			ObjectUtils.disposeObject(_track);
			_track = null;
			_trackClickTimer = null;
			super.dispose();
		}
		/**
		 * 
		 * @param stylename 向下按钮的style通过ComponentFactory创建并赋值给downButton
		 * 
		 */		
		public function set downButtonStyle(stylename:String):void
		{
			if(_decreaseButtonStyle == stylename) return;
			_decreaseButtonStyle = stylename;
			increaseButton = ComponentFactory.Instance.creat(_decreaseButtonStyle);
		}
		
		public function getModel():BoundedRangeModel
		{
			return _model;
		}
		
		public function getThumbVisible():Boolean
		{
			return _thumb.visible;
		}
		/**
		 * 
		 * @param display 向下按钮
		 * 
		 */		
		public function set increaseButton(display:BaseButton):void
		{
			if(_increaseButton == display) return;
			if(_increaseButton)_increaseButton.removeEventListener(Event.CHANGE,__decreaseButtonClicked);
			ObjectUtils.disposeObject(_increaseButton);
			_increaseButton = display;
			if(_increaseButton)_increaseButton.pressEnable = true;
			if(_increaseButton)_increaseButton.addEventListener(Event.CHANGE,__decreaseButtonClicked);
			onPropertiesChanged(P_increaseButton);
		}
		public function set increaseButtonInnerRectString(value:String):void
		{
			if(_increaseButtonInnerRectString == value)return;
			_increaseButtonInnerRectString = value;
			_increaseButtonInnerRect = ClassUtils.CreatInstance(ClassUtils.INNERRECTANGLE,ComponentFactory.parasArgs(_increaseButtonInnerRectString));
			onPropertiesChanged(P_increaseButtonInnerRect);
		}
		
		/**
		 * 
		 * @param stylename 向下按钮的样式
		 * 
		 */		
		public function set increaseButtonStyle(stylename:String):void
		{
			if(_increaseButtonStyle == stylename) return;
			_increaseButtonStyle = stylename;
			increaseButton = ComponentFactory.Instance.creat(_increaseButtonStyle);
		}
		
		public function isVertical():Boolean
		{
			return _orientation == 0;
		}
		
		public function get maximum():int
		{
			return getModel().getMaximum();
		}
		public function set maximum(maximum:int):void
		{
			if(getModel().getMaximum() == maximum)return;
			getModel().setMaximum(maximum);
			onPropertiesChanged(P_maximum);
		}
		
		public function get minimum():int
		{
			return getModel().getMinimum();
		}
		public function set minimum(minimum:int):void
		{
			if(getModel().getMinimum() == minimum)return;
			getModel().setMinimum(minimum);
			onPropertiesChanged(P_minimum);
		}
		
		public function get orientation():int
		{
			return _orientation;
		}
		
		public function set orientation(ori:int):void
		{
			if(_orientation == ori) return;
			_orientation = ori;
			onPropertiesChanged(P_orientation);
		}
		
		public function removeStateListener(listener:Function):void{
			removeEventListener(InteractiveEvent.STATE_CHANGED, listener);
		}
		
		public function get scrollValue():int
		{
			return getModel().getValue();
		}
		
		public function set scrollValue(v:int):void
		{
			getModel().setValue(v);
			onPropertiesChanged(P_scrollValue);
		}
		
		public function setupModel(model:BoundedRangeModel):void
		{
			if(_model)
			{
				_model.removeStateListener(__onModelChange);
			}else
			{
				_model = model;
			}
			_model.addStateListener(__onModelChange);
		}
		
		/**
		 * 
		 * @param display 滚动条的拖动条
		 * 
		 */		
		public function set thumb(display:BaseButton):void
		{
			if(_thumb == display) return;
			if(_thumb)_thumb.removeEventListener(MouseEvent.MOUSE_DOWN,__onThumbDown);
			ObjectUtils.disposeObject(_thumb);
			_thumb = display;
			if(_thumb)_thumb.addEventListener(MouseEvent.MOUSE_DOWN,__onThumbDown);
			onPropertiesChanged(P_thumb);
		}
		public function set thumbAreaInnerRectString(value:String):void
		{
			if(_thumbAreaInnerRectString == value)return;
			_thumbAreaInnerRectString = value;
			_thumbAreaInnerRect = ClassUtils.CreatInstance(ClassUtils.INNERRECTANGLE,ComponentFactory.parasArgs(_thumbAreaInnerRectString));
			onPropertiesChanged(P_thumbAreaInnerRect);
		}
		
		public function get thumbMinSize():int
		{
			return _thumbMinSize;
		}
		
		/**
		 * 
		 * @param value 拖动条的最小像素值，
		 * 当是VScrollbar时此值指的是拖动条的最小高度
		 * 当是HScrollbar时此值指的是拖动条的最小宽度
		 */		
		public function set thumbMinSize(value:int):void
		{
			if(_thumbMinSize == value)return;
			_thumbMinSize = value;
			onPropertiesChanged(P_thumbMinSize);
		}
		
		/**
		 * 
		 * @param stylename 拖动条的样式，通过ComponentFactory创建
		 * 
		 */		
		public function set thumbStyle(stylename:String):void
		{
			if(_thumbStyle == stylename) return;
			_thumbStyle = stylename;
			thumb = ComponentFactory.Instance.creat(_thumbStyle);
		}
		/**
		 * 
		 * @param display 拖动条的背景
		 * 
		 */		
		public function set track(display:DisplayObject):void
		{
			if(_track == display) return;
			if(_track && _track is InteractiveObject)
			{
				_track.removeEventListener(MouseEvent.MOUSE_DOWN,__onTrackClickStart);
				_track.removeEventListener(MouseEvent.MOUSE_UP,__onTrackClickStop);
				_track.removeEventListener(MouseEvent.MOUSE_OUT,__onTrackClickStop);
			}
			ObjectUtils.disposeObject(_track);
			_track = display;
			if(_track is InteractiveObject)InteractiveObject(_track).mouseEnabled = true;
			_track.addEventListener(MouseEvent.MOUSE_DOWN,__onTrackClickStart);
			_track.addEventListener(MouseEvent.MOUSE_UP,__onTrackClickStop);
			_track.addEventListener(MouseEvent.MOUSE_OUT,__onTrackClickStop);
			onPropertiesChanged(P_track);
		}
		public function set trackInnerRectString(value:String):void
		{
			if(_trackInnerRectString == value)return;
			_trackInnerRectString = value;
			_trackInnerRect = ClassUtils.CreatInstance(ClassUtils.INNERRECTANGLE,ComponentFactory.parasArgs(_trackInnerRectString));
			onPropertiesChanged(P_trackInnerRect);
		}
		/**
		 * 
		 * @param stylename 拖动条的背景样式
		 * 
		 */		
		public function set trackStyle(stylename:String):void
		{
			if(_trackStyle == stylename) return;
			_trackStyle = stylename;
			track = ComponentFactory.Instance.creat(_trackStyle);
		}
		
		public function get unitIncrement():int
		{
			return _unitIncrement;
		}
		public function set unitIncrement(unitIncrement:int):void
		{
			_unitIncrement = unitIncrement;
		}
		
		public function get valueIsAdjusting():Boolean{
			return getModel().getValueIsAdjusting();
		}
		public function set valueIsAdjusting(b:Boolean):void
		{
			if(getModel().getValueIsAdjusting() == b)return;
			getModel().setValueIsAdjusting(b);
			onPropertiesChanged(P_valueIsAdjusting);
		}
		
		public function get visibleAmount():int
		{
			return getModel().getExtent();
		}
		public function set visibleAmount(extent:int):void
		{
			if(getModel().getExtent() == extent)return;
			getModel().setExtent(extent);
			onPropertiesChanged(P_visibleAmount);
		}
		
		protected function __decreaseButtonClicked(event:Event):void
		{
			scrollByIncrement(_unitIncrement);
		}
		
		protected function __increaseButtonClicked(event:Event):void
		{
			scrollByIncrement(-_unitIncrement);
		}
		
		protected function __onModelChange(event:InteractiveEvent):void
		{
			dispatchEvent(new InteractiveEvent(InteractiveEvent.STATE_CHANGED));
		}
		
		protected function __onScrollValueChange(event:InteractiveEvent):void
		{
			if(!_isDragging)updatePosByScrollvalue();
		}
		
		protected function __onThumbDown(event:MouseEvent):void
		{
			valueIsAdjusting = true;
			var mp:Point = getMousePosition();
			var mx:int = mp.x;
			var my:int = mp.y;
			if(isVertical()){
				_thumbDownOffset = my - _thumb.y;
			}else{
				_thumbDownOffset = mx - _thumb.x;
			}
			_isDragging = true;
			StageReferance.stage.addEventListener(MouseEvent.MOUSE_UP,__onThumbUp);
			StageReferance.stage.addEventListener(MouseEvent.MOUSE_MOVE,__onThumbMoved);
		}
		
		protected function __onThumbMoved(event:MouseEvent):void
		{
			scrollThumbToCurrentMousePosition();
			event.updateAfterEvent();
		}
		
		protected function __onThumbUp(event:MouseEvent):void
		{
			_isDragging = false;
			StageReferance.stage.removeEventListener(MouseEvent.MOUSE_UP,__onThumbUp);
			StageReferance.stage.removeEventListener(MouseEvent.MOUSE_MOVE,__onThumbMoved);
		}
		
		protected function __onTrackClickStart(event:MouseEvent):void
		{
			_currentTrackClickDirction = getValueWithPosition(getMousePosition()) > scrollValue ? 1 : -1;
			scrollToAimPoint(getMousePosition());
			_trackClickTimer.addEventListener(TimerEvent.TIMER,__onTrackPressed);
			_track.addEventListener(MouseEvent.MOUSE_UP,__onTrackClickStop);
			_track.addEventListener(MouseEvent.MOUSE_OUT,__onTrackClickStop);
			_trackClickTimer.start();
		}
		
		protected function __onTrackClickStop(event:MouseEvent):void
		{
			_trackClickTimer.stop();
			_trackClickTimer.removeEventListener(TimerEvent.TIMER,__onTrackPressed);
			_track.removeEventListener(MouseEvent.MOUSE_UP,__onTrackClickStop);
			_track.removeEventListener(MouseEvent.MOUSE_OUT,__onTrackClickStop);
		}
		
		protected function __onTrackPressed(event:TimerEvent):void
		{
			scrollToAimPoint(getMousePosition());
		}
		
		override protected function addChildren():void
		{
			super.addChildren();
			if(_track)addChild(_track);
			if(_increaseButton)addChild(_increaseButton);
			if(_decreaseButton)addChild(_decreaseButton);
			if(_thumb)addChild(_thumb);
		}
		
		protected function getValueWithPosition(point:Point):int{
			var mx:int = point.x;
			var my:int = point.y;
			var thumbScrollRect:Rectangle = _thumbAreaInnerRect.getInnerRect(_width,_height);
			var thumbMin:int, thumbMax:int, thumbPos:int;
			if(isVertical())
			{
				thumbMin = thumbScrollRect.y;
				thumbMax = thumbScrollRect.y + thumbScrollRect.height - _thumbRect.height;
				thumbPos = my;
			}else
			{
				thumbMin = thumbScrollRect.x;
				thumbMax = thumbScrollRect.x + thumbScrollRect.width - _thumbRect.width;
				thumbPos = mx;
			}
			return getValueWithThumbMaxMinPos(thumbMin, thumbMax, thumbPos);
		}
		
		override protected function init():void
		{
			setupModel(new BoundedRangeModel());
			_thumbRect = new Rectangle();
			_trackClickTimer = new Timer(ComponentSetting.BUTTON_PRESS_STEP_TIME);
			addStateListener(__onScrollValueChange);
			super.init();
		}
		
		protected function layoutComponent():void
		{
			DisplayUtils.layoutDisplayWithInnerRect(_increaseButton,_increaseButtonInnerRect,_width,_height);
			DisplayUtils.layoutDisplayWithInnerRect(_decreaseButton,_decreaseButtonInnerRect,_width,_height);
			DisplayUtils.layoutDisplayWithInnerRect(_track,_trackInnerRect,_width,_height);
		}
		
		override protected function onProppertiesUpdate():void
		{
			super.onProppertiesUpdate();
			if(_changedPropeties[Component.P_height] || _changedPropeties[Component.P_width] ||
			_changedPropeties[P_decreaseButtonInnerRect] || _changedPropeties[P_increaseButtonInnerRect] ||
			_changedPropeties[P_trackInnerRect] || _changedPropeties[P_thumbAreaInnerRect])
			{
				layoutComponent();
			}
			if(_changedPropeties[P_maximum] || _changedPropeties[P_minimum] || _changedPropeties[P_scrollValue] ||
				_changedPropeties[P_valueIsAdjusting] || _changedPropeties[P_visibleAmount] || _changedPropeties[P_thumbAreaInnerRect] ||
				_changedPropeties[P_thumbMinSize] || _changedPropeties[P_thumb])
			{
				updatePosByScrollvalue();
			}
		}
		
		protected function scrollByIncrement(value:int):void
		{
			scrollValue = scrollValue + value;
		}
		
		protected function scrollThumbToCurrentMousePosition():void
		{
			var mp:Point = getMousePosition();
			var mx:int = mp.x;
			var my:int = mp.y;
			var thumbScrollRect:Rectangle = _thumbAreaInnerRect.getInnerRect(_width,_height);
			var thumbMin:int, thumbMax:int, thumbPos:int;
			
			if(isVertical()){
				thumbMin = thumbScrollRect.y;
				thumbMax = thumbScrollRect.y + thumbScrollRect.height - _thumbRect.height;
				thumbPos = Math.min(thumbMax, Math.max(thumbMin, (my - _thumbDownOffset)));
				setThumbPosAndSize(_thumbRect.x,thumbPos,_thumbRect.width,_thumbRect.height);
			}else{
				thumbMin = thumbScrollRect.x;
				thumbMax = thumbScrollRect.x + thumbScrollRect.width - _thumbRect.width;
				thumbPos = Math.min(thumbMax, Math.max(thumbMin, (mx - _thumbDownOffset)));
				setThumbPosAndSize(thumbPos,_thumbRect.y,_thumbRect.width,_thumbRect.height);
			}
			var scrollBarValue:int = getValueWithThumbMaxMinPos(thumbMin, thumbMax, thumbPos);
			scrollValue = scrollBarValue;
		}
		
		protected function scrollToAimPoint(pt:Point):void
		{
			var scrollContinueDestination:int = getValueWithPosition(pt);
			var currentIncrement:int;
			if(scrollContinueDestination > scrollValue && _currentTrackClickDirction > 0){
				currentIncrement = blockIncrement;
			}else if(scrollContinueDestination < scrollValue && _currentTrackClickDirction < 0){
				currentIncrement = -blockIncrement;
			}else
			{
				return;
			}
			scrollByIncrement(currentIncrement);
		}
		
		protected function setThumbPosAndSize(posX:int,posY:int,sizeW:int,sizeH:int):void
		{
			_thumbRect.x = _thumb.x = posX;
			_thumbRect.y = _thumb.y = posY;
			_thumbRect.width = _thumb.width = sizeW;
			_thumbRect.height = _thumb.height= sizeH;
		}
		
		protected function updatePosByScrollvalue():void
		{
			var min:int = minimum;
			var extent:int = visibleAmount;
			var range:int = maximum - min;
			var value:int = scrollValue;
			var thumbRect:Rectangle = _thumbAreaInnerRect.getInnerRect(_width,_height);
			if(range <= 0)
			{
				_thumb.visible = false;
				return;
			}
			var trackLength:int;
			var thumbLength:int;
			if(isVertical()){
				trackLength = thumbRect.height;
				thumbLength = Math.floor(trackLength*(extent/range));
				_thumb.visible = thumbLength >0 && thumbLength < thumbRect.height;
			}else{
				trackLength = thumbRect.width;
				thumbLength = Math.floor(trackLength*(extent/range));
				_thumb.visible = thumbLength < thumbRect.width;
			}
			_increaseButton.mouseEnabled = _decreaseButton.mouseEnabled = _thumb.visible;
			if(trackLength > thumbMinSize){
				thumbLength = Math.max(thumbLength, thumbMinSize);
			}else{
				_thumb.visible = false;
				return;
			}
			_increaseButton.mouseEnabled = _decreaseButton.mouseEnabled = _thumb.visible;
			var thumbRange:int = trackLength - thumbLength;
			var thumbPos:int;
			if((range - extent) == 0){
				thumbPos = 0;
			}else{
				thumbPos = Math.round(thumbRange * ((value - min) / (range - extent)));
			}
			if(isVertical()){
				setThumbPosAndSize(thumbRect.x,thumbPos + thumbRect.y,thumbRect.width,thumbLength)
			}else{
				setThumbPosAndSize(thumbRect.x + thumbPos,thumbRect.y,thumbLength,thumbRect.height);
			}
		}
		
		private function getValueWithThumbMaxMinPos(thumbMin:int, thumbMax:int, thumbPos:int):int{
			var scrollBarValue:int;
			if (thumbPos >= thumbMax) {
				scrollBarValue = _model.getMaximum() - _model.getExtent();
			}else{
				var valueMax:int = _model.getMaximum() - _model.getExtent();
				var valueRange:int = valueMax - _model.getMinimum();
				var thumbValue:int = thumbPos - thumbMin;
				var thumbRange:int = thumbMax - thumbMin;
				var value:int = Math.round((thumbValue / thumbRange) * valueRange);
				scrollBarValue = value + _model.getMinimum();
			}
			return scrollBarValue;    	
		}
	}
}